
#include "user_password.h"

#if GKT_USER_PASSWORD_DYNAMIC_ENABLE \
	&& USER_DYNPWD_NOTS_ENABLE

#define USER_DYNPWD_NOTS_DTU_MINUTE_STEPS_SHIFT	0
#define USER_DYNPWD_NOTS_DTU_HOUR_SHIFT	\
	(USER_DYNPWD_NOTS_DTU_MINUTE_STEPS_SHIFT + USER_DYNPWD_NOTS_MINUTE_STEPS_BITS)
#define USER_DYNPWD_NOTS_DTU_DAY_SHIFT	\
	(USER_DYNPWD_NOTS_DTU_HOUR_SHIFT + USER_DYNPWD_NOTS_HOUR_BITS)
#define USER_DYNPWD_NOTS_DTU_MONTH_SHIFT	\
	(USER_DYNPWD_NOTS_DTU_DAY_SHIFT + USER_DYNPWD_NOTS_DAY_BITS)
#define USER_DYNPWD_NOTS_DTU_YEAR_OFFSET_SHIFT	\
	(USER_DYNPWD_NOTS_DTU_MONTH_SHIFT + USER_DYNPWD_NOTS_MONTH_BITS)

int user_dynpwd_NOTS_retrieve(user_dynpwd_data_s *data,
				uint32_t *values)
{
	gkt_date_time_s date_time;
	uint32_t year_offset, minute_steps;
	uint32_t tag;

	data->nots.total_times = USER_DYNPWD_NOTS_FIELD(values[0], TIMES);
	if (!data->nots.total_times || (data->nots.total_times > USER_DYNPWD_NOTS_TIMES_MAX)) {
		gkt_trace("user_dynpwd_nots: times(%u) invalid!\n", data->nots.total_times);
		return GKT_EPARAM;
	}

	minute_steps = USER_DYNPWD_NOTS_FIELD(values[0], MINUTE_STEPS);
	if (minute_steps >= USER_DYNPWD_NOTS_MINUTE_STEPS_MAX) {
		gkt_trace("user_dynpwd_nots: minute_steps(%u) invalid!\n", minute_steps);
		return GKT_EPARAM;
	}

	date_time.time.hour = USER_DYNPWD_NOTS_FIELD(values[0], HOUR);
	if (date_time.time.hour >= GKT_DT_DAY_HOURS) {
		gkt_trace("user_dynpwd_nots: hour(%u) invalid!\n", date_time.time.hour);
		return GKT_EPARAM;
	}

	year_offset = USER_DYNPWD_NOTS_FIELD(values[0], YEAR_OFFSET);
	if (year_offset >= USER_DYNPWD_NOTS_YEAR_OFFSET_MAX) {
		gkt_trace("user_dynpwd_nots: year_offset(%u) invalid!\n", year_offset);
		return GKT_EPARAM;
	}

	date_time.date.day = USER_DYNPWD_NOTS_FIELD(values[0], DAY);
	date_time.date.month = USER_DYNPWD_NOTS_FIELD(values[0], MONTH);
	date_time.date.year = year_offset + USER_DYNPWD_BASE_YEAR;
	if (!gkt_dt_check_date(&date_time.date)) {
		gkt_trace("user_dynpwd_nots: date(%04u-%02u-%02u) invalid!\n", 
			date_time.date.day, date_time.date.month,
			date_time.date.year);
		return GKT_EPARAM;
	}

	tag = USER_DYNPWD_NOTS_FIELD(values[0], TAG);
	if (tag != USER_DYNPWD_NOTS_TAG_VALUE) {
		gkt_trace("user_dynpwd_nots: tag(%u) invalid!\n", tag);
		return GKT_EPARAM;
	}

	data->nots.dtu = (minute_steps << USER_DYNPWD_NOTS_DTU_MINUTE_STEPS_SHIFT)
				| (date_time.time.hour << USER_DYNPWD_NOTS_DTU_HOUR_SHIFT)
				| (date_time.date.day << USER_DYNPWD_NOTS_DTU_DAY_SHIFT)
				| (date_time.date.month << USER_DYNPWD_NOTS_DTU_MONTH_SHIFT)
				| (year_offset << USER_DYNPWD_NOTS_DTU_YEAR_OFFSET_SHIFT);

	return GKT_SUCCESS;
}

int user_dynpwd_NOTS_verify(user_dynpwd_data_s *data, uint32_t user_index)
{
	const user_dynpwd_item_s *c_item;
	uint32_t item_index, offset;
	uint16_t left_times, left_times_bits;
	uint8_t state;
	int retval;

	gkt_info("user_dynpwd_verify: <NOTS> (%d) dtu(0x%x) nbr(%u)\n",
				data->cancel, data->nots.dtu, data->nots.total_times);

	item_index = 0;
	while (item_index < g_user_dynpwd.instance.item_available_nums) {
		c_item = USER_DYNPWD_ITEM_C(item_index);
		if ((user_index == (uint32_t)c_item->ext_header.user_index)
			&& (USER_DYNPWD_TYPE_NOTS == c_item->ext_header.dynpwd_type)
			&& (data->nots.dtu == c_item->nots.dtu)
			&& (data->nots.total_times == (uint32_t)c_item->nots.total_times))
		{
			gkt_info("user_dynpwd_verify: <NOTS> I(%u) ST(%02x) T(%02x @ %02x)\n",
				item_index, c_item->ext_header.state,
				c_item->nots.total_times, c_item->nots.left_times_bits);

			if (USER_ITEM_ST_USED == c_item->ext_header.state) {
				left_times_bits = c_item->nots.left_times_bits;
				if (left_times_bits > 0) {
					if (!data->cancel) {
						left_times_bits >>= 1;
						offset = USER_ITEM_OFFSET(&g_user_dynpwd.instance, item_index)
										+ __MEMBER_OFFSET(user_dynpwd_item_s, nots.left_times_bits);
						retval = gkt_flash_partition_write(g_user_dynpwd.instance.c_flash_info->id,
										offset, sizeof(left_times_bits), &left_times_bits, 0);
						if (retval != GKT_SUCCESS)
							gkt_error("user_dynpwd_verify: <NOTS> update left_times_bits failed %d\n", retval);

						if (!left_times_bits)
							user_update_item_state(&g_user_dynpwd.instance, item_index, USER_ITEM_ST_CANCELLED);

						return GKT_USER_VERIFY_PASSED;
					}
					else {
						gkt_trace("user_dynpwd_verify: <NOTS> *** CANCEL ***\n");
						user_update_item_state(&g_user_dynpwd.instance, item_index, USER_ITEM_ST_CANCELLED);
						return GKT_USER_VERIFY_CANCELLED;
					}
				}
				else {
					/* last state update operation is not complete */
					user_update_item_state(&g_user_dynpwd.instance, item_index, USER_ITEM_ST_CANCELLED);
				}
			}

			return GKT_USER_VERIFY_FAILED;
		}

		item_index++;
	}

	gkt_info("user_dynpwd_verify: <NOTS> *** NEW ***\n");

	g_user_dynpwd.save_item.ext_header.user_index = user_index;
	g_user_dynpwd.save_item.ext_header.dynpwd_type = USER_DYNPWD_TYPE_NOTS;
	g_user_dynpwd.save_item.nots.dtu = data->nots.dtu;
	g_user_dynpwd.save_item.nots.total_times = data->nots.total_times;
	left_times = data->nots.total_times - 1;
	g_user_dynpwd.save_item.nots.left_times_bits = (1U << left_times) - 1;

	state = (data->cancel || !left_times) ? USER_ITEM_ST_CANCELLED : USER_ITEM_ST_USED;
	retval = user_dynpwd_save_item(&g_user_dynpwd.save_item, state);
	if (data->cancel)
		retval = GKT_USER_VERIFY_CANCELLED;
	else if (GKT_SUCCESS == retval)
		retval = GKT_USER_VERIFY_PASSED;
	else
		retval = GKT_USER_VERIFY_FAILED;

	return retval;
}

#endif

